﻿/**
* CRL
*/
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Data;

namespace CRL.Data.LambdaQuery.Mapping
{

    public struct ColumnType
    {
        public string name;
        public int index;
        public string typeName;
    }
    public class DataContainer
    {
        internal IDataReader reader;
        Type dataType;
        Dictionary<string, ColumnType> columnMapping;
        //Dictionary<int, Type> columnMapping2 = new Dictionary<int, Type>();
        internal int currentDataIndex = 0;

        internal object GetCurrentData()
        {
            return reader[currentDataIndex];
        }
        static Dictionary<Type, int> dataTypeIndexDic = new Dictionary<Type, int>();
        static Dictionary<int, Type> dataTypeIndexDic2 = new Dictionary<int, Type>();
        static DataContainer()
        {
            dataTypeIndexDic.Add(typeof(System.String), 1);
            dataTypeIndexDic.Add(typeof(System.Decimal), 2);
            dataTypeIndexDic.Add(typeof(System.Double), 3);
            dataTypeIndexDic.Add(typeof(System.Single), 4);
            dataTypeIndexDic.Add(typeof(System.Boolean), 5);
            dataTypeIndexDic.Add(typeof(System.Int32), 6);
            dataTypeIndexDic.Add(typeof(System.Int16), 7);
            dataTypeIndexDic.Add(typeof(System.Enum), 8);
            dataTypeIndexDic.Add(typeof(System.Byte), 9);
            dataTypeIndexDic.Add(typeof(System.DateTime), 10);
            dataTypeIndexDic.Add(typeof(System.UInt16), 11);
            dataTypeIndexDic.Add(typeof(System.Int64), 12);
            dataTypeIndexDic.Add(typeof(System.Object), 13);
            dataTypeIndexDic.Add(typeof(System.Byte[]), 14);
            dataTypeIndexDic.Add(typeof(System.Guid), 15);
            foreach (var kv in dataTypeIndexDic)
            {
                dataTypeIndexDic2.Add(kv.Value, kv.Key);
            }
        }
        public DataContainer(IDataReader _reader, Type _dataType, Dictionary<string, ColumnType> _columnMapping)
        {
            dataType = _dataType;
            reader = _reader;
            columnMapping = _columnMapping;
        }
        //public static int GetDataTypeIndex(Attribute.FieldInnerAttribute attr)
        //{
        //    if (attr.ColumnDataType == null)
        //    {
        //        return -1;
        //    }
        //    dataTypeIndexDic.TryGetValue(attr.ColumnDataType, out var index);
        //    return index;
        //}
        Type GetDataTypeByIndex(int index)
        {
            dataTypeIndexDic2.TryGetValue(index, out var type);
            return type;
        }
        internal ColumnType _GetCurrentColumnName()
        {
            foreach (var kv in columnMapping)
            {
                if (kv.Value.index == currentDataIndex)
                {
                    return kv.Value;
                }
            }
            return new ColumnType();
        }
        #region method
        Dictionary<int, Type> dbFieldTypes = new Dictionary<int, Type>();
        void setCurrentDataIndex(int index, out Type dbFieldType)
        {
            var a = dbFieldTypes.TryGetValue(index, out dbFieldType);
            if (!a)
            {
                dbFieldType = reader.GetFieldType(index);
                dbFieldTypes[index] = dbFieldType;
            }
            currentDataIndex = index;
        }
        public TEnum GetEnum<TEnum>(int index) where TEnum : struct
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(TEnum);
            }
            var value = reader.GetValue(index);
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }
        public TEnum? GetEnumNullable<TEnum>(int index) where TEnum : struct
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(TEnum);
            }
            var value = reader.GetValue(index);
            return (TEnum)Enum.ToObject(typeof(TEnum), value);
        }

        public short GetInt16(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(short);
            }
            if (dbFieldType != typeof(short))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt16(value);
            }
            return reader.GetInt16(index);
        }
        public short? GetInt16Nullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(short))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt16(value);
            }
            return reader.GetInt16(index);
        }
        public int GetInt32(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(int);
            }
            if (dbFieldType != typeof(int))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt32(value);
            }
            return reader.GetInt32(index);
        }
        public int? GetInt32Nullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(int))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt32(value);
            }
            return reader.GetInt32(index);
        }
        public long GetInt64(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(long);
            }
            if (dbFieldType != typeof(long))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt64(value);
            }
            return reader.GetInt64(index);
        }
        public long? GetInt64Nullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(long))
            {
                var value = reader.GetValue(index);
                return Convert.ToInt64(value);
            }
            return reader.GetInt64(index);
        }
        public decimal GetDecimal(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(decimal);
            }
            if (dbFieldType != typeof(decimal))
            {
                var value = reader.GetValue(index);
                return Convert.ToDecimal(value);
            }
            return reader.GetDecimal(index);
        }
        public decimal? GetDecimalNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(decimal))
            {
                var value = reader.GetValue(index);
                return Convert.ToDecimal(value);
            }
            return reader.GetDecimal(index);
        }
        public double GetDouble(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(double);
            }
            if (dbFieldType != typeof(double))
            {
                var value = reader.GetValue(index);
                return Convert.ToDouble(value);
            }
            return reader.GetDouble(index);
        }
        public double? GetDoubleNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(double))
            {
                var value = reader.GetValue(index);
                return Convert.ToDouble(value);
            }
            return reader.GetDouble(index);
        }
        public float GetFloat(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(float);
            }
            if (dbFieldType != typeof(float))
            {
                var value = reader.GetValue(index);
                return Convert.ToSingle(value);
            }
            return reader.GetFloat(index);
        }
        public float? GetFloatNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(float))
            {
                var value = reader.GetValue(index);
                return Convert.ToSingle(value);
            }
            return reader.GetFloat(index);
        }
        public bool GetBoolean(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(bool);
            }
            if (dbFieldType != typeof(bool))
            {
                var value = reader.GetValue(index);
                return Convert.ToBoolean(value);
            }
            return reader.GetBoolean(index);
        }
        public bool? GetBooleanNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(bool))
            {
                var value = reader.GetValue(index);
                return Convert.ToBoolean(value);
            }
            return reader.GetBoolean(index);
        }
        public DateTime GetDateTime(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(DateTime);
            }
            try
            {
                if (dbFieldType != typeof(DateTime))
                {
                    var value = reader.GetValue(index);
                    return Convert.ToDateTime(value);
                }
                return reader.GetDateTime(index);
            }
            catch
            {
                //mysql 无法识别参数化的变量
                return Convert.ToDateTime(reader.GetValue(index));
            }
        }
        public DateTime? GetDateTimeNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(DateTime))
            {
                var value = reader.GetValue(index);
                return Convert.ToDateTime(value);
            }
            return reader.GetDateTime(index);
        }
        public Guid GetGuid(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(Guid);
            }
            if (dbFieldType != typeof(Guid) && dbFieldType != typeof(byte[]))
            {
                var value = reader.GetValue(index);
                return Guid.Parse(value.ToString());
            }
            return reader.GetGuid(index);
        }
        public Guid? GetGuidNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(byte[]))
            {
                var value = reader.GetValue(index);
                return Guid.Parse(value.ToString());
            }
            return reader.GetGuid(index);
        }
        public byte GetByte(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(byte);
            }
            if (dbFieldType != typeof(byte))
            {
                var value = reader.GetValue(index);
                return Convert.ToByte(value);
            }
            return reader.GetByte(index);
        }
        public byte? GetByteNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(byte))
            {
                var value = reader.GetValue(index);
                return Convert.ToByte(value);
            }
            return reader.GetByte(index);
        }
        public char GetChar(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(char);
            }
            if (dbFieldType != typeof(char))
            {
                var value = reader.GetValue(index);
                return Convert.ToChar(value);
            }
            return reader.GetChar(index);
        }
        public char? GetCharNullable(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(char))
            {
                var value = reader.GetValue(index);
                return Convert.ToChar(value);
            }
            return reader.GetChar(index);
        }
        public string GetString(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            if (dbFieldType != typeof(string))
            {
                var value = reader.GetValue(index);
                return value.ToString();
            }
            return reader.GetString(index);
        }
        public object GetIndexValue(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return null;
            }
            return reader.GetValue(index);
        }
        public T GetStringObj<T>(int index)
        {
            setCurrentDataIndex(index, out var dbFieldType);
            if (reader.IsDBNull(index))
            {
                return default(T);
            }
            var value = reader.GetString(index);
            if (string.IsNullOrEmpty(value))
            {
                return default(T);
            }
            return (T)SettingConfig.ValueObjDeserialize(value, typeof(T));
            //return Core.Extension.Extension.ToObject<T>(value);
        }
        #endregion
        static Dictionary<Type, MethodInfo> methods = new Dictionary<Type, MethodInfo>();
        public static MethodInfo GetMethod(bool valueNeedConvert, Type propType, bool anonymousClass = false)
        {
            var typeDataContainer = typeof(DataContainer);
            if (valueNeedConvert)
            {
                //强制转换
                var method = typeDataContainer.GetMethod(nameof(DataContainer.GetStringObj), BindingFlags.Instance | BindingFlags.Public);
                method = method.MakeGenericMethod(new Type[] { propType });
                return method;
            }
            var unType = Nullable.GetUnderlyingType(propType);
            var isNullable = unType != null;
            MethodInfo result;
            if (propType.IsEnum && !anonymousClass)
            {
                propType = propType.GetEnumUnderlyingType();
            }

            if (methods.Count == 0)
            {
                var array = typeDataContainer.GetMethods(BindingFlags.Instance | BindingFlags.Public);
                foreach (var item in array)
                {
                    if (item.Name == "GetHashCode")
                    {
                        continue;
                    }
                    if (!item.Name.StartsWith("Get"))
                    {
                        continue;
                    }
                    methods.Add(item.ReturnType, item);
                }
            }
            if (propType.IsEnum || unType?.IsEnum == true)
            {
                var m1 = typeDataContainer.GetMethod("GetEnum");
                var m2 = typeDataContainer.GetMethod("GetEnumNullable");
                if (isNullable)
                {
                    return m2.MakeGenericMethod(unType);
                }
                return m1.MakeGenericMethod(propType);
            }

            var a = methods.TryGetValue(propType, out result);
            if (a)
            {
                return result;
            }
            if (propType == typeof(Guid))
            {
                result = typeDataContainer.GetMethod("GetGuid");
            }
            return result;
        }
    }
}
